#ifndef _WORLD_PACKETS_H
#define _WORLD_PACKETS_H

class DLL_EXPORT RealmPacket
{
	std::vector<ui8> _data;
	ui16 _opcode, _size;
public:
	RealmPacket(ui16 opcode, ui16 size = 0) : _opcode(opcode), _size(size)
	{
	}

	ui8* GetSendable()
	{
		_size = GetSize();
		ui8* _send = new ui8[GetSize() + 4];
		*(ui16*)_send = ntohs(_size + 2);
		*(ui16*)(_send + 2) = _opcode;
		if(_size)
			memcpy(_send + 4, Contents(), GetSize());
		return _send;
	}

	void Clear()
	{
		_size = _opcode = 0;
		_data.clear();
	}

	void SetSize(ui16 new_size)
	{
		_size = new_size;
	}

	void SetOpcode(ui16 new_opcode)
	{
		_opcode = new_opcode;
	}

	ui16 GetOpcode() const
	{
		return _opcode;
	}

	RealmPacket& operator<<(ui8 data)
	{
		_data.push_back(data);
		return (*this);
	}

	RealmPacket& operator<<(ui16 data)
	{
		ui8* d = (ui8*)&data;
		_data.push_back(*d);
		_data.push_back(*(d + 1));
		return (*this);
	}

	RealmPacket& operator<<(ui32 data)
	{
		ui8* d = (ui8*)&data;
		for(int i = 0; i < 4; ++i)
			_data.push_back(*(d + i));
		return (*this);
	}

	RealmPacket& operator<<(i32 data)
	{
		ui8* d = (ui8*)&data;
		for(int i = 0; i < 4; ++i)
			_data.push_back(*(d + i));
		return (*this);
	}

	RealmPacket& operator<<(ui64 data)
	{
		ui8* d = (ui8*)&data;
		for(int i = 0; i < 8; ++i)
			_data.push_back(*(d + i));
		return (*this);
	}

	RealmPacket& operator<<(char* data)
	{
		ui8* d = (ui8*)data;
		for( ; *d; ++d)
			_data.push_back(*d);
		_data.push_back('\0');
		return (*this);
	}

	RealmPacket& operator<<(float data)
	{
		ui8* d = (ui8*)&data;
		for(int i = 0; i < 4; ++i)
			_data.push_back(*(d + i));
		return (*this);
	}

	RealmPacket& operator<<(double data)
	{
		ui8* d = (ui8*)&data;
		for(int i = 0; i < 8; ++i)
			_data.push_back(*(d + i));
		return (*this);
	}

	RealmPacket& operator<<(const char* data)
	{
		(*this) << (char*)data;
		return *this;
	}

	RealmPacket& operator<<(WoWString& data)
	{
		return (*this) << data.finish();
	}

	RealmPacket& operator<<(const WoWGuid& value) 
	{
		_data.push_back(value.GetNewGuidMask());
		Append((ui8*)value.GetNewGuid(), value.GetNewGuidLen());
		return *this;
	}

	RealmPacket& operator>>(ui32& out)
	{
		if(GetSize() < 4)
			return (*this);
		ui8* d = &_data.front();
		for(int i = 0; i < 4; ++i)
			*(((ui8*)&out) + i) = *(d + i);
		for(int i = 0; i < 4; ++i)
			_data.erase(_data.begin());
		return (*this);
	}

	RealmPacket& operator>>(ui16& out)
	{
		if(GetSize() < 2)
			return (*this);
		ui8* d = (ui8*)&out;
		*d = *(&_data.front());
		_data.erase(_data.begin());
		*(d + 1) = *(&_data.front());
		_data.erase(_data.begin());
		return (*this);
	}

	RealmPacket& operator>>(ui8& out)
	{
		if(!GetSize())
			return (*this);
		out = *(&_data.front());
		_data.erase(_data.begin());
		return (*this);
	}

	RealmPacket& operator>>(i8& out)
	{
		if(!GetSize())
			return (*this);
		out = *(&_data.front());
		_data.erase(_data.begin());
		return (*this);
	}

	RealmPacket& operator>>(char* out)
	{
		ui8* d = &_data.front();
		ui8* o = (ui8*)out;
		int i = 0;
		for(; GetSize() && *d; ++i)
		{
			*(o + i) = *d;
			_data.erase(_data.begin());
		}
		o[i] = '\0';
		_data.erase(_data.begin());
		return (*this);
	}

	RealmPacket& operator>>(float& out)
	{
		ui8* o = (ui8*)&out;
		ui8* d = Contents();
		for(int i = 0 ; GetSize() && i < 4; ++i)
		{
			*(o + i) = *d;
			_data.erase(_data.begin());
		}
		return (*this);
	}

	RealmPacket& operator>>(ui64& out)
	{
		ui8* o = (ui8*)&out;
		ui8* d = Contents();
		for(int i = 0 ; GetSize() && i < 8; ++i)
		{
			*(o + i) = *d;
			_data.erase(_data.begin());
		}
		return (*this);
	}

	RealmPacket& operator>>(WoWGuid& out)
	{
		ui8 mask, value;
		(*this) >> mask;
		out.Init(mask);
		ui8 len = 0;
		for(register ui8 i = 0; i < 8; ++i)
			if(mask & (1 << i))
				++len;
		for(register ui8 i = 0; i < len; ++i)
		{
			(*this) >> value;
			out.AppendField(value);
		}
		return (*this);
	}

	__inline ui8* Contents()
	{
		if(!GetSize())
			return 0;
		return (ui8*)(&_data.front());
	}

	void Append(ui8* data, size_t size)
	{
		for(ui32 i = 0; i < size; ++i)
			_data.push_back(*(data + i));
	}

	void Resize(size_t sz)
	{
		_data.resize(sz);
	}

	size_t GetSize()
	{
		return _data.size();
	}

	void Read(ui8* dest, size_t size)
	{
		for(ui32 i = 0; i < size; ++i)
			*(dest + i) = *(Contents() + i);
	}

	void RemoveData(size_t amount)
	{
		for(register ui32 i = 0; i < amount && GetSize(); ++i)
			_data.erase(_data.begin());
	}
};

#endif